package com.modular.iot.opt;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.PageResult;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.StaticLog;
import com.ToolOpt;
import com.c.opt.ComOpt;
import com.c.opt.query.ComOptQueryWrapper;
import com.modular.event.tool.ToolRain;
import com.modular.iot.bo.*;
import ntd.util.ToolLock;
import ntd.util.UtilThreadPool;
import ntd.util.config.common.bo.PageBO;
import ntd.util.frame.data.tool.ToolMysql;
import ntd.util.frame.task.handle.fm.util.ToolFmData;
import ntd.util.modular.things.entity.Device;
import ntd.util.modular.things.entity.DeviceData;
import ntd.util.modular.things.entity.DeviceDataItem;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

public class OptIotData {

    private ComOpt<DeviceData> deviceDataComOpt = ToolOpt.OPT_T_D_DATA;
    private ComOpt<DeviceDataItem> deviceDataItemComOpt = ToolOpt.OPT_T_D_D_ITEM;
    private OptIotDevice iotDeviceOpt = ToolOpt.OPT_T_DEVICE;

    /**
     * 保存iot数据
     * @param iotDataIn
     */
    public void saveIotData(IotData iotDataIn) {
        Opt.ofNullable(iotDataIn).ifPresent(iotData -> {
            // 保存主数据记录
            DeviceData deviceData = initDataRow(iotData);

            // 补充雨量站处理逻辑 - 放入下雨级别因子(这里判定有液位的为需要补充雨量因子) todo
            try {
                IotDataItem f10011 = CollUtil.findOne(iotData.getIotDataItems(), iotDataItem ->
                        ObjectUtil.isNotNull(iotDataItem.getFactorCode()) && iotDataItem.getFactorCode().equals("f10011"));
                if (ObjectUtil.isNotNull(f10011)) {
                    Opt.ofNullable(rainHandle()).ifPresent(iotDataItem -> iotData.getIotDataItems().add(iotDataItem));
                }
            } catch (Exception e) {
                StaticLog.error(e, "[iot][数据保存][{}]失败...", JSONUtil.toJsonStr(iotData.getIotDataItems()));
            }

            // 保存子数据记录
            iotData.getIotDataItems().forEach(iotDataItem -> initDataItem(iotDataItem, deviceData.getId(), deviceData.getDeviceCode()));

            // 富铭租户特殊逻辑处理
            if ("40289f5a7e94f892017e951800160000".equals(iotDataIn.getTenantId())) {
                // 数据同步推送
                UtilThreadPool.run(() -> ToolFmData.OPT_FM_DATA.pushData(iotData));
            }
        });
    }

    /**
     * 补充雨污站下雨因子
     * @return
     */
    public IotDataItem rainHandle() {

        // 基站天气计算 - 过去12小时 最大值 - 最小值 - 比对雨量表
        Double rainfall = ToolOpt.OPT_T_DATA.queryRainfall(null);
        if (ObjectUtil.isNull(rainfall)) {
            return null;
        }
        Integer rain = ToolRain.genRainLevel(rainfall);
        IotDataItem iotDataItem = new IotDataItem();
        iotDataItem.setFactorCode("f11002");
        iotDataItem.setFactorValRtd(String.valueOf(rain));

        // 这里计算今天降雨
        ToolOpt.OPT_T_SCENE_WEATHER.weatherCurrDay("1", rain);

        return iotDataItem;
    }

    /**
     * 保存iot数据
     * @param iotModelBo
     */
    public void saveIot(IotModelBo iotModelBo) {
        // 初始化设备信息
        Device device = iotDeviceOpt.initDevice(iotModelBo);
        // 保存数据信息
        saveIotData(iotModelBo.getIotData());
        // 保存配置信息
        ToolOpt.OPT_T_D_CONFIG.updateComConfig(iotModelBo.getCode(),
                JSONUtil.toJsonStr(iotModelBo.getAttributes()));
        // 保存属性信息
//        for (IotAttribute attribute : iotModelBo.getAttributes()) {
//            attribute.setDeviceId(device.getId());
//            ToolOpt.OPT_T_ATTRIBUTE.syncAttribute(attribute);
//        }
    }

    /**
     * 初始化数据详情
     * @param iotDataItem
     * @param dataId
     * @param deviceCode
     * @return
     */
    public DeviceDataItem initDataItem(IotDataItem iotDataItem, String dataId, String deviceCode) {
        AtomicReference<DeviceDataItem> result = new AtomicReference<>();
        ToolLock.lockRun("initDataItem" + dataId + iotDataItem.getFactorCode(), () -> {
            ComOptQueryWrapper queryWrapper = deviceDataItemComOpt.query().eq("data_id", dataId).eq("factor_code", iotDataItem.getFactorCode());
            DeviceDataItem deviceDataItem = deviceDataItemComOpt.getBean(queryWrapper);
            if (ObjectUtil.isNull(deviceDataItem)) {
                deviceDataItem = iotDataItem.toDeviceDataItemDo();
                deviceDataItem.setDataId(dataId);
                deviceDataItem.setDeviceCode(deviceCode);
            } else {
                BeanUtil.copyProperties(iotDataItem, deviceDataItem);
            }
            deviceDataItem.setId(deviceDataItemComOpt.syncBean(deviceDataItem));
            result.set(deviceDataItem);
        });
        return result.get();
    }

    /**
     * 初始化主数据记录
     * @param iotData
     * @return
     */
    public DeviceData initDataRow(IotData iotData) {
        String localDateTime = DateUtil.formatLocalDateTime(iotData.getReportTime());
        AtomicReference<DeviceData> result = new AtomicReference<>();
        ToolLock.lockRun("initDataRow" + iotData.getMn() + iotData.getCn() + localDateTime, () -> {
            ComOptQueryWrapper queryWrapper = deviceDataComOpt.query()
                    .eq("report_time", localDateTime)
                    .eq("cn", iotData.getCn())
                    .eq("device_code", iotData.getMn());
            DeviceData deviceData = deviceDataComOpt.getBean(queryWrapper);
            if (ObjectUtil.isNull(deviceData)) {
                deviceData = new DeviceData();
                deviceData.setReportTime(iotData.getReportTime());
                deviceData.setDeviceCode(iotData.getMn());
                deviceData.setCn(iotData.getCn());
                deviceData.setSt(iotData.getSt());
                deviceData.setId(deviceDataComOpt.syncBean(deviceData));
            }
            result.set(deviceData);
        });
        return result.get();
    }

    /**
     * iot数据查询
     *
     * @param iotDataQueryInBo
     * @return
     */
    public PageBO<IotData> queryIotData(IotDataQueryInBo iotDataQueryInBo) {
        // 查询主数据
        ComOptQueryWrapper queryWrapper = deviceDataComOpt.queryWrapper();
        queryWrapper.eq("device_code", iotDataQueryInBo.getDeviceCode());
        queryWrapper.eq("cn", iotDataQueryInBo.getCn());
        Opt.ofEmptyAble(iotDataQueryInBo.getDeviceCodes()).ifPresent(rows -> queryWrapper.in("device_code", rows));
        PageResult<DeviceData> deviceDataList = deviceDataComOpt.pageBean(queryWrapper, iotDataQueryInBo.getPage(), iotDataQueryInBo.getSize());

        if (CollUtil.isEmpty(deviceDataList)) {
            return new PageBO<>(ListUtil.of(), 0);
        }

        int total = deviceDataList.getTotal();

        List<IotData> iotDataList = buildIotData(iotDataQueryInBo, deviceDataList);

        return new PageBO<>(iotDataList, total);
    }

    public Double queryRainfall(String deviceCode) {
        String sql = "select MAX(tddi.factor_val_rtd) - MIN(tddi.factor_val_rtd) as rainfall from things_device_data tdd INNER JOIN things_device_data_item tddi ON (tdd.id = tddi.data_id) WHERE tdd.report_time > (NOW() - interval 12 hour) and cn = '2011' and tddi.factor_code = 'f11001';";
        try {
            if (StrUtil.isNotBlank(deviceCode)) {
                sql += StrUtil.format(" and tdd.device_code = '{}'", deviceCode);
            }
            return NumberUtil.toDouble(Opt.ofNullable(deviceDataComOpt.db().queryNumber(sql)).orElse(0));
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public List<IotData> queryNewData(IotDataQueryInBo iotDataQueryInBo) {
        String sql = "select tdd.* from things_device_data tdd INNER JOIN (select device_code, MAX(report_time) time from things_device_data WHERE report_time > (NOW() - interval 4 hour) and cn = '2011' {} GROUP BY device_code) t ON (tdd.device_code = t.device_code AND tdd.report_time = t.time);";

        List<String> codes = new ArrayList<>();
        Opt.ofEmptyAble(iotDataQueryInBo.getDeviceCodes()).ifPresent(codes::addAll);
        Opt.ofBlankAble(iotDataQueryInBo.getDeviceCode()).ifPresent(codes::add);

        if (CollUtil.isNotEmpty(codes)) {
            sql = StrUtil.format(sql, " and device_code in " + ToolMysql.listToSqlStr(codes));
        } else {
            sql = StrUtil.format(sql, "");
        }

        try {
            List<DeviceData> deviceDataList = deviceDataComOpt.db().query(sql, DeviceData.class);

            List<IotData> iotDataList = buildIotData(iotDataQueryInBo, deviceDataList);

            return iotDataList;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    private List<IotData> buildIotData(IotDataQueryInBo iotDataQueryInBo, List<DeviceData> deviceDataList) {
        // 查询对应设备信息
        List<Device> deviceList = ToolOpt.OPT_T_DEVICE.query().in("code", CollStreamUtil.toList(deviceDataList, DeviceData::getDeviceCode)).listBean();
        Map<String, Device> deviceMap = CollStreamUtil.toIdentityMap(deviceList, Device::getCode);

        // 查询子数据
        List<DeviceDataItem> deviceDataItems = deviceDataItemComOpt.query().in("data_id", CollStreamUtil.toList(deviceDataList, DeviceData::getId)).listBean();
        // 根据主数据id分组
        Map<String, List<DeviceDataItem>> deviceDataByDataIdMap = CollStreamUtil.groupByKey(deviceDataItems, DeviceDataItem::getDataId);


        List<IotData> iotDataList = CollStreamUtil.toList(deviceDataList, deviceData -> {
            IotData iotData = IotData.transformComPlatform(deviceData);

            if (iotDataQueryInBo.getIsFm()) {
                // 富铭特殊逻辑封装
                Opt.ofNullable(ToolFmData.findSiteInfo(iotData.getMn())).ifPresent(entity -> Opt.ofBlankAble(entity.getStr("site_name")).ifPresent(iotData::setName));
            }
            // 封装状态信息
            iotData.setStatus(deviceMap.get(deviceData.getDeviceCode()).getStatus());

            iotData.setIotDataItems(CollStreamUtil.toList(deviceDataByDataIdMap.get(deviceData.getId()), IotDataItem::transformComPlatform));
            return iotData;
        });
        return iotDataList;
    }

    public static void main(String[] args) {
//        List<IotData> iotData = ToolOpt.OPT_T_DATA.queryNewData(new IotDataQueryInBo());
//        StaticLog.info(JSONUtil.toJsonStr(iotData));

        StaticLog.info(String.valueOf(ToolOpt.OPT_T_DATA.queryRainfall("41010501G00002")));
    }

}
